﻿using AspectCore.DynamicProxy;
using AspectCore.Injector;
using CacheManager;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace CacheAOP_AspectCore.AOPCache
{
    public class CachingInterceptor : AbstractInterceptor
    {
        [FromContainer]
        public ICacheManager _cacheProvider { get; set; }

        /// <summary>
        /// Invoke the specified context and next.
        /// </summary>
        /// <returns>The invoke.</returns>
        /// <param name="context">Context.</param>
        /// <param name="next">Next.</param>
        public async override Task Invoke(AspectContext context, AspectDelegate next)
        {
            var qCachingAttribute = GetCachingAttributeInfo(context.ServiceMethod);
            if (qCachingAttribute != null)
            {
                await ProceedCaching(context, next, qCachingAttribute);
            }
            else
            {
                await next(context);
            }
        }

        /// <summary>
        /// Gets the QCaching attribute info.
        /// </summary>
        /// <returns>The QC aching attribute info.</returns>
        /// <param name="method">Method.</param>
        private RedisCacheAttribute GetCachingAttributeInfo(MethodInfo method)
        {
            return method.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(RedisCacheAttribute)) as RedisCacheAttribute;
        }

        /// <summary>
        /// Proceeds the caching.
        /// </summary>
        /// <param name="invocation">Invocation.</param>
        /// <param name="attribute">Attribute.</param>
        private async Task ProceedCaching(AspectContext context, AspectDelegate next, RedisCacheAttribute attribute)
        {
            var cacheKey = attribute.CacheKey;

            var cacheValue = _cacheProvider.Get(cacheKey);
            if (cacheValue != null)
            {
                context.ReturnValue = cacheValue;
                return;
            }

            await next(context);

            if (!string.IsNullOrWhiteSpace(cacheKey))
            {
                _cacheProvider.Set(cacheKey, context.ReturnValue, attribute.Expiration);
            }
        }

    }
}
